package com.college.intercept;

import java.sql.Connection;
import java.util.Properties;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.RowBounds;
import org.apache.log4j.Logger;

@Intercepts(@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }))
public class PaginationInterceptor implements Interceptor{
	
	private final static String SQL_SELECT_REGEX = "(?is)^\\s*SELECT.*$";
    private final static String SQL_COUNT_REGEX = "(?is)^\\s*SELECT\\s+COUNT\\s*\\(\\s*(?:\\*|\\w+)\\s*\\).*$";

    private final static Logger log = Logger.getLogger(PaginationInterceptor.class);
	
	@Override
	public Object intercept(Invocation inv) throws Throwable {
		StatementHandler target = (StatementHandler)inv.getTarget();
		BoundSql boundSql = target.getBoundSql();
		String sql = boundSql.getSql();
		if(StringUtils.isBlank(sql)){
			return inv.proceed();
		}
		//log.info("origin sql>>>>>"+sql.replaceAll("\n", ""));
		
		if(sql.matches(SQL_SELECT_REGEX)&& !Pattern.matches(SQL_COUNT_REGEX, sql)){
			
			Object obj = FieldUtils.readField(target, "delegate", true);
			
			RowBounds rowBounds = (RowBounds) FieldUtils.readField(obj,
                    "rowBounds", true);
			
			if (rowBounds != null && rowBounds != RowBounds.DEFAULT) {
				
                FieldUtils.writeField(boundSql, "sql", buildPageSqlForMysql(sql, rowBounds),
                        true);
                
                //log.info("new sql>>>>>"+ boundSql.getSql().replaceAll("\n", ""));

                FieldUtils.writeField(rowBounds, "offset",
                        RowBounds.NO_ROW_OFFSET, true);
                FieldUtils.writeField(rowBounds, "limit",
                        RowBounds.NO_ROW_LIMIT, true);
            }
		}
		
		return inv.proceed();
	}
	
	@Override
	public Object plugin(Object target){
		return Plugin.wrap(target, this);
	}
	
	@Override
	public void setProperties(Properties arg0){
		//log.info("Query Args: "+arg0);
	}
	
	public String buildPageSqlForOracle(String oldSql, RowBounds rowBounds) {
		oldSql = oldSql.trim();
		//System.out.println("offset=" + rowBounds.getOffset() + ",limit="+rowBounds.getLimit());
		int start = (rowBounds.getOffset() - 1) * rowBounds.getLimit() + 1;
		int end = start + rowBounds.getLimit() - 1;
		
		StringBuffer pagingSelect = new StringBuffer(oldSql.length() +  100);  
		pagingSelect.append("select mTabB.* from(select rownum num,mTabA.* from(");  
	    pagingSelect.append(oldSql);
	    pagingSelect.append(")mTabA)mTabB where mTabB.num<=");
	    pagingSelect.append(end);
	    pagingSelect.append(" and mTabB.num>=");
	    pagingSelect.append(start);
	    
	    
	    //System.out.println(pagingSelect.toString());
        return pagingSelect.toString();
        		    
	}
	
	
    public StringBuilder buildPageSqlForMysql(String oldSql, RowBounds rowBounds) {
        StringBuilder pageSql = new StringBuilder(100);
        //String beginrow = String.valueOf((page.getCurrentPage() - 1) * page.getPageSize());
        
        String beginRow = String.valueOf(( rowBounds.getOffset() -1 ) * rowBounds.getLimit()); 
        pageSql.append(oldSql);
        pageSql.append(" limit " + beginRow + "," + rowBounds.getLimit());
        return pageSql;
    }

}
